Skip to content

fix(esp32): boot ESP32-P4 eco2 + validate flash offsets (#278, #279)#281

Merged
zackees merged 5 commits into
mainfrom
fix/esp32-p4-eco2-and-flash-offset-validation
May 29, 2026
Merged

fix(esp32): boot ESP32-P4 eco2 + validate flash offsets (#278, #279)#281
zackees merged 5 commits into
mainfrom
fix/esp32-p4-eco2-and-flash-offset-validation

Conversation

@zackees
Copy link
Copy Markdown
Member

@zackees zackees commented May 29, 2026

Summary

  • ESP32-P4 (eco2) won't boot: bootloader flashed at 0x0 (should be 0x2000) + eco5-linked bootloader/app crash on eco2 silicon #278 — ESP32-P4 (eco2) now boots. Bootloader offset fixed (0x00x2000 for P4 and C5, matching the ROM), and the eco0–eco2 boards now select the esp32p4_es SDK (base ROM) via a new chip_variant/sdk_variant() board mechanism, so the bootloader entry no longer hits an Illegal instruction panic and the app's segments don't overlap the eco2 bootloader stack. Verified live on the attached eco2 board: clean boot:0x30f (SPI_FAST_FLASH_BOOT) + entry + 80+ Hello from ESP32-P4! lines from tests/platform/esp32p4 over COM25.
  • test: validate per-chip flash offsets against an authoritative source (recurring ROM-offset bug) #279 — fail-loud + CI guardrail against the recurring flash-offset class of bug. The ESP32 deploy path no longer silently falls back to esp32's 0x1000 on an unknown MCU (it would brick every RISC-V variant); it now errors clearly. A new ci/check_flash_offsets.py cross-checks every esp32*.json bootloader/partitions/firmware offset against the authoritative arduino-esp32 boards.txt (build.bootloader_addr, with the platform.txt default applied for chips that don't override). Wired into validate-boards.yml via --download.
  • Incidental: PyO3 cdylib doctest fix so cargo test --workspace passes; one rustfmt drift on zccache.rs.

Test plan

  • soldr cargo check -p fbuild-daemon -p fbuild-build --all-targets — clean.
  • soldr cargo test -p fbuild-config -p fbuild-build --lib — passes, incl. test_bootloader_flash_offsets_match_rom, test_esp32p4_evboard_uses_es_chip_variant, test_esp32p4_r3_uses_eco5_chip_variant, test_sdk_variant_falls_back_to_mcu.
  • soldr cargo test -p fbuild-daemon deploy — 10 passed.
  • uv run python ci/check_flash_offsets.py — all 9 chips PASS against the installed framework.
  • Simulated-mutation negative test: temporarily setting esp32p4 bootloader back to 0x0 makes the check exit 1 with esp32p4: bootloader offset '0x0' != authoritative '0x2000'; reverted.
  • Live hardware: fbuild deploy tests/platform/esp32p4 -e esp32p4 -p COM25 succeeds (full flash) on the attached ESP32-P4-EV (eco2, rev v1.3); the demo .ino runs and prints Hello from ESP32-P4! continuously on the USB-Serial/JTAG console.
  • CI green on this branch (will be confirmed by the workflow run).

Closes #278.
Refs #279 (this PR implements items 1, 3, and 4 of the sub-issue checklist; item 2 — adding an esp32c61 config — was intentionally deferred because the installed arduino-esp32 framework ships no esp32c61 SDK libs, so a config would be unbuildable; the deploy fail-loud change handles c61 safely by erroring with a clear message).

🤖 Generated with Claude Code

zackees and others added 5 commits May 29, 2026 13:18
ESP32-P4 (eco2, rev v1.3) wouldn't boot under fbuild. Two distinct bugs,
the second masked by the first; both fixed here, both verified on hardware
(boot console captured from COM25: SPI_FAST_FLASH_BOOT + clean entry,
80+ `Hello from ESP32-P4!` lines from the demo .ino).

Bug 1 -- bootloader flashed at the wrong offset.
The ROM expects the second-stage bootloader at 0x2000 on ESP32-P4 (and
ESP32-C5); fbuild was flashing it at 0x0, so the ROM read garbage and
reboot-looped with `invalid header: 0x47550ff7`. Set the correct offset
in esp32p4.json and esp32c5.json, and added test_bootloader_flash_offsets_match_rom
asserting every esp32 config against the known ROM values.

Bug 2 -- bootloader + app linked for the wrong ROM revision.
The framework ships two ESP32-P4 SDK variants: `esp32p4` (eco5, chip rev
301-399) and `esp32p4_es` (eco0-eco2, rev 1-199). fbuild always picked
`esp32p4`, so the eco5-linked bootloader's entry was an illegal
instruction on eco2 silicon, and the app's segments overlapped the eco2
bootloader stack. Mirror upstream arduino-esp32 by introducing a
`build.chip_variant` board field + `BoardConfig::sdk_variant()` accessor:
the "ES pre rev.300" boards (esp32-p4-evboard, esp32-p4, m5stack-tab5-p4)
select `esp32p4_es`; `esp32-p4_r3` keeps `esp32p4`. All ESP32 SDK
accessors (includes, libs, ld_flags/scripts/libs, defines, bootloader
ELF, partitions) now resolve through sdk_variant(); the build fingerprint
includes board_chip_variant so the cache invalidates correctly. esptool
`--chip` and the per-MCU compiler/linker config still key on `mcu`.

The P4 acceptance test (tests/platform/esp32p4) now sets
ARDUINO_USB_CDC_ON_BOOT=1 + ARDUINO_USB_MODE=1 so Serial reaches the
board's USB-Serial/JTAG port (the only host interface), making the
"output on the monitor" success path actually observable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lash

The ESP32 deploy path silently swallowed `get_mcu_config` errors and fell
back to the esp32 MCU config, meaning an unsupported/new ESP32 variant
got flashed with esp32's 0x1000 bootloader offset -- wrong for every
RISC-V variant (need 0x0) and C5/P4 (need 0x2000). The result was a
device that wouldn't boot (`invalid header` reboot loop until reflashed).

Replace the `unwrap_or_else(get_mcu_config("esp32"))` fallback with
`map_err(|e| FbuildError::DeployFailed(...))?` so the deploy fails with
a clear "unsupported ESP32 MCU '<x>' for board '<id>' ... cannot
determine flash offsets" instead of silently misflashing. The build path
already propagates this error with `?`; only the deploy path was unsafe.

This is the highest-value item in #279 -- it converts every coverage gap
(e.g. esp32-c61-devkitc1-n8r2 today, any future variant tomorrow) from a
silent brick-until-reflash into a loud error.

Refs #279.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds `ci/check_flash_offsets.py`, a CI guardrail that cross-checks every
`crates/fbuild-build/src/esp32/configs/esp32*.json` bootloader / partitions
/ firmware offset against the authoritative arduino-esp32 `boards.txt`
(`<chip>.build.bootloader_addr`, with the `platform.txt` default applied
to chips that don't override -- e.g. esp32/esp32s2). Hard-fails on
mismatch, on a config chip unknown to boards.txt, on missing offsets, and
on `partitions != 0x8000` / `firmware != 0x10000`. Prints a per-chip
PASS/FAIL table.

The authoritative source is resolved in priority order: `--boards-txt`
explicit, `$FBUILD_ESP32_BOARDS_TXT`, fbuild's installed framework cache,
or `--download` from the pioarduino release tag for CI. Wired into
`.github/workflows/validate-boards.yml` via `--download` since the
validate-boards job doesn't pre-install the ESP32 framework.

Closes the recurring class of bug behind #278: a future ESP32 variant
shipping a wrong/absent bootloader offset can no longer reach CI green.

Refs #279.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`cargo test --workspace` was failing on `Doc-tests fbuild_python` with
"can't find crate for `pyo3`": rustdoc compiles the cdylib's lib.rs as a
doctest harness, but PyO3's `extension-module` feature suppresses the
libpython link, so the pyo3 macros fail to resolve. This is the standard
PyO3 cdylib limitation; there are no `///` examples in this crate
anyway. Setting `doctest = false` on `[lib]` is PyO3's documented fix
and lets the full workspace test suite pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Multi-line `assert_eq!` reformatting picked up by `cargo fmt` under the
current toolchain. Pure formatting, no behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@zackees, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 39 minutes and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e2c9a091-97b3-4224-9827-3968e1a43b61

📥 Commits

Reviewing files that changed from the base of the PR and between 9520ceb and 5f50662.

📒 Files selected for processing (21)
  • .github/workflows/validate-boards.yml
  • ci/check_flash_offsets.py
  • crates/fbuild-build/src/esp32/configs/esp32c5.json
  • crates/fbuild-build/src/esp32/configs/esp32p4.json
  • crates/fbuild-build/src/esp32/mcu_config.rs
  • crates/fbuild-build/src/esp32/orchestrator/boot_artifacts.rs
  • crates/fbuild-build/src/esp32/orchestrator/build.rs
  • crates/fbuild-build/src/esp32/orchestrator/fingerprint.rs
  • crates/fbuild-build/src/zccache.rs
  • crates/fbuild-config/assets/boards/json/esp32-p4-evboard.json
  • crates/fbuild-config/assets/boards/json/esp32-p4.json
  • crates/fbuild-config/assets/boards/json/esp32-p4_r3.json
  • crates/fbuild-config/assets/boards/json/m5stack-tab5-p4.json
  • crates/fbuild-config/src/board/db.rs
  • crates/fbuild-config/src/board/loaders.rs
  • crates/fbuild-config/src/board/methods.rs
  • crates/fbuild-config/src/board/tests.rs
  • crates/fbuild-config/src/board/types.rs
  • crates/fbuild-daemon/src/handlers/operations/deploy.rs
  • crates/fbuild-python/Cargo.toml
  • tests/platform/esp32p4/platformio.ini
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/esp32-p4-eco2-and-flash-offset-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zackees zackees merged commit 5cb265a into main May 29, 2026
74 of 83 checks passed
@zackees zackees deleted the fix/esp32-p4-eco2-and-flash-offset-validation branch May 29, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ESP32-P4 (eco2) won't boot: bootloader flashed at 0x0 (should be 0x2000) + eco5-linked bootloader/app crash on eco2 silicon

1 participant